#import genericpyogppath
import sys
sys.path[0:0] = [
  '/Users/lawsonenglish/libdev/eggs/setuptools-0.6c9-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/indra.base-1.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/uuid-1.30-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/wsgiref-0.1.2-py2.5.egg',
  '/Users/lawsonenglish/libdev/src/pyogp.lib.base',
  '/Users/lawsonenglish/libdev/src/pyogp.interop',
  '/Users/lawsonenglish/libdev/eggs/indra.ipc-1.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/grokcore.component-1.5.1-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/WebOb-0.9.3-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.component-3.5.1-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.interface-3.4.1-py2.5-macosx-10.3-fat.egg',
  '/Users/lawsonenglish/libdev/eggs/indra.util-1.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.event-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.configuration-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/martian-0.11-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.i18nmessageid-3.4.3-py2.5-macosx-10.3-fat.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.proxy-3.4.2-py2.5-macosx-10.3-fat.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.security-3.5.2-py2.5-macosx-10.3-fat.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.deferredimport-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.deprecation-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.schema-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.location-3.4.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.exceptions-3.5.2-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/pytz-2008c-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.testing-3.7.0-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.traversing-3.5.0a4-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.publisher-3.5.4-py2.5.egg',
  '/Users/lawsonenglish/libdev/eggs/zope.i18n-3.5.0-py2.5.egg',
  ]


import uuid
from threading import Thread
import signal

from pyogp.lib.base.credentials import PlainPasswordCredential

import sys
for elements in sys.path: print elements


from pyogp.lib.base.agentdomain import AgentDomain
from pyogp.lib.base.regiondomain import Region
from pyogp.lib.base import registration

from pyogp.lib.base.interfaces import IPlaceAvatar, IEventQueueGet

from pyogp.lib.base.message.udpdispatcher import UDPDispatcher
from pyogp.lib.base.message.message import Message, Block
from pyogp.lib.base.message.interfaces import IHost
from pyogp.lib.base.message.types import MsgType

from zope.component import provideUtility
from pyogp.lib.base.network.interfaces import IUDPClient
from pyogp.lib.base.network.net import NetUDPClient

import getpass, sys, logging
from optparse import OptionParser

import wx, datetime, time

import pprint

Running = None

anum = 0

def add_to_list(L): 
    """decorator to be used with class methodlister.
    It adds the immediately following method/function 
    to the list of methods note that 'methods' is really 
    a class variable or a global variable in the module.
    I modded the class it works with to allow more than one 
    method list per class which allows us to keep discrete steps in the process
    listed in different lists. YOu could also use decorator
    outside of classes to sequence arbitrary lists of functions
    in a global list or lists.
    """
    def deco(func):        
        L.append(func)
        return func
    return deco

class methodlister(object):
    """subclass this class to work 
    with methods from one or more more monolithc functions that need 
    to be dealt with as small bits for 'threading' purposes. Lets the
    logger log events with more granularity. Would allow a cancel or 
    perhaps an "undo" during login or or other user actions/sequences if we wanted...
    ...I think.
    """
    #methods = []  #one or more method-lists should be declared as class variables in the subclass
                                       
    
    def doall(self,list): 
        """this executes all methods in a list in sequence. Error checking would be less
        useful this way, but might be worth doing in certain circumstances --basically
        would recreate the original monlithic function/method as a single call"""
        [func(self) for func in list]  
                                       
                                      
    #@add_to_list(methods)
        """place this decorator with the target list ahead of each method you want listed
        """
    def donextmethod(self,list, index):      
        """what the name says. keep track of the index at the caller level. 
        The index could be tracked internally as well if that would make things tidier in the code"""                                        
        list[index](self)
        
    def listerlen(self,list):
        """Just returns the lenght of the specified list"""
        return len(list)

class LoggerTextCntl(wx.TextCtrl):
    def write(self, text):
        """
        Write the text to the LoggerTextCntl instance
        If not called in the context of the gui thread then uses
        CallAfter to do the work there.
        """        

        if not wx.Thread_IsMain():
            wx.CallAfter(self.__write, text)
        else:
            self.__write(text)

    def __write(self, text):
        # helper function for actually writing the text.
        self.AppendText(text)

    def flush(self):
        pass

class TestFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(1200, 700))
        panel = wx.Panel(self)
        self.timertext  = LoggerTextCntl(panel, 1, "",
                                 style=wx.TE_MULTILINE|wx.TE_READONLY, size = (150,400))
        self.msgtext  = LoggerTextCntl(panel, 2, "",
                                 style=wx.TE_MULTILINE|wx.TE_READONLY, pos = (151,0),size = (1049,400))
        self.errtext  = LoggerTextCntl(panel, 3, "",
                                 style=wx.TE_MULTILINE|wx.TE_READONLY, pos = (151,401),size = (1049,200))
        self.mybutton = wx.Button(panel, 4, 'Pause/Resume', (50, 620))
        self.Bind(wx.EVT_BUTTON, self.OnPause, id=4)
        self.Centre()
        self.eqg = None
        self.gotimer =True
        self.timer = wx.Timer(self)
        
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
        self.timer.Start(100)
        self.Show(True)
   
    
    def OnTimer(self, event):
        wx.GetApp().DoTimer(event)
    def OnPause(self, event):
        
        if self.gotimer == True:
            self.timer.Stop()
            self.gotimer = False
        else:
            self.timer.Start(100)
            self.gotimer = True
       
    def onCloseWindow(self,event):
        #self.timer.Stop()
        self.eqg.contenew = False
        wx.GetApp().avatar_obj.tearDown()
        sys.stdout = sys.stderr = wx.GetApp().savedstdio
        i = 0
        while i<20:
            print i
            print self.eqg
            if not self.eqg.isAlive():break
            time.sleep(1)
            i +=1
        
        logging.shutdown()
        self.Destroy()
class avatar_obj(methodlister):
    """login and place and etc an agent
    each method represents a diffrent bit of the original
    functions, so that it can be run one-step-at-a-time
    in an idle or timer loop from the wx main window or other thread/loop.
    """ 
    loginmethods = []
    place_avatarmethods = []
    def __init__(self, zapp):
        self.zapp = zapp
        
    @add_to_list(loginmethods)
    def meth1(self): 
        #sets up logging instance for the avatar instance"""
        self.console = logging.StreamHandler(self.zapp.frame.msgtext)  #blah.msgtext is a file-like
                                                                       #text control defined in the main window
        self.console.setLevel(logging.DEBUG)                           # handler settings further refine logger settings
        formatter = logging.Formatter('%(name)-30s: %(levelname)-8s %(message)s')
        self.console.setFormatter(formatter)

        self.logger = logging.getLogger("pyogp.lib.base.wxexample")   #we need to establish the naming convention
                                                                      #a.b.c... used in pyogp loggers so we can refer to loggers
                                                                      #without passing logging objects around
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(self.console)
  
   
    @add_to_list(loginmethods)
    def meth2(self): 
        #This should be replaced by by obtaining GUI or command line-set variables"""
        self.firstname = "First"
        self.lastname = "Last"
        self.password = "Password"
        self.login_uri = 'https://login1.aditi.lindenlab.com/cgi-bin/auth.cgi'        
        self.start_region_uri = 'http://sim1.vaak.lindenlab.com:13000'
        self.target_region_uri = 'http://sim1.vaak.lindenlab.com:13001'
        
        
    @add_to_list(loginmethods)
    def meth3(self): 
        #initial login sequence"""
        self.credentials = PlainPasswordCredential(self.firstname, self.lastname, self.password)
    
        self.agentdomain = AgentDomain(self.login_uri)
        self.agent = self.agentdomain.login(self.credentials)
    
        self.logger.info("logged in, we now have an agent: %s" %self.agent)
        

        
    @add_to_list(loginmethods)
    def meth4(self):          
    # now get an event_queue_get cap
        self.eqgCAP = IEventQueueGet(self.agentdomain)
        
        self.logger.info("received an event queue cap: %s", self.eqgCAP.cap)

    @add_to_list(loginmethods)
    def meth5(self): 
        # spawn the AD event queue thread
        
        
        thread1logger = logging.getLogger("thread1")
        logging.getLogger("thread1").setLevel(logging.DEBUG)
        thread1logger.addHandler(self.console)
        
        self.eqg = self.zapp.frame.eqg = eventQueueGet(self.eqgCAP, thread1logger)
        self.eqg.start()
 
    @add_to_list(loginmethods)
    def cleanuploginmeth(self):  
        #whatever might need to be done at end of sequence
        self.logger.debug("Cleaning up login")
        #self.start_region_uri = self.start_region_uri 
     

    """Place avatar on region. As above, encapsulates
    methods one logical/logging chunk at a time for
    extra debugging/prototyping goodness. Replaces
    self.connection call in original test code.
    """
    
    @add_to_list(place_avatarmethods)  
    def place_avatarmeth1(self):
        # initial setup for place avatar
        provideUtility(NetUDPClient(), IUDPClient)
        self.messenger = UDPDispatcher()
        
    @add_to_list(place_avatarmethods)
    def place_avatarmeth2(self):
        # place avatar step
        self.logger = logging.getLogger("pyogp.lib.base.wxexample")
        self.place = IPlaceAvatar(self.agentdomain)
        self.region = Region(self.start_region_uri)
        self.logger.debug("now we try to place the avatar on a region")
        
        
    @add_to_list(place_avatarmethods)
    def place_avatarmeth3(self):   
        # hopefully worked so obtain relevant vars
        self.avatar = self.place(self.region, [5,5,5])
        self.vanishing_data = {}
        self.vanishing_data['agent_id'] = self.avatar.region.details['agent_id']
        self.vanishing_data['session_id'] = self.avatar.region.details['session_id']
        self.vanishing_data['circuit_code'] = self.avatar.region.details['circuit_code']        
         
        self.logger.debug("got region details: %s", str(self.avatar.region.details))
        
    @add_to_list(place_avatarmethods)
    def place_avatarmeth4(self):
        #begin UDP communication
        self.host = IHost((self.avatar.region.details['sim_ip'],
                    self.avatar.region.details['sim_port']))
        
        self.logger.debug("got host: %s", self.host)
        
    @add_to_list(place_avatarmethods)
    def place_avatarmeth5(self):
                #SENDS UseCircuitCode
        msg = Message('UseCircuitCode',
                      Block('CircuitCode', Code=self.avatar.region.details['circuit_code'],
                            SessionID=uuid.UUID(self.vanishing_data['session_id'] ),
                            ID=uuid.UUID(self.vanishing_data['agent_id'])))
        self.messenger.send_reliable(msg, self.host, 0)
        self.logger.debug("sending message: %s", "UseCircuitCode in Meth4")
        time.sleep(1)
 
    @add_to_list(place_avatarmethods)
    def place_avatarplace_avatarmeth6(self): 
        #SENDS CompleteAgentMovement
        msg = Message('CompleteAgentMovement',
                      Block('AgentData', AgentID=uuid.UUID(self.vanishing_data['agent_id']),
                            SessionID=uuid.UUID(self.vanishing_data['session_id']),
                            CircuitCode=self.avatar.region.details['circuit_code']))
        self.messenger.send_reliable(msg, self.host, 0)
        self.logger.debug("sending message: %s", "CompleteAgentMovement in Meth5")

    @add_to_list(place_avatarmethods)
    def place_avatarmeth7(self): 
        #SENDS UUIDNameRequest
        msg = Message('UUIDNameRequest',
                      Block('UUIDNameBlock', ID=uuid.UUID(self.vanishing_data['agent_id'])
                            )
                      )
        self.messenger.send_message(msg, self.host)  
        self.logger.debug("sending message: %s", "UUIDNameRequest in Meth6")
        
    @add_to_list(loginmethods)
    def cleanup_place_avatar_meth(self):  
        #whatever might need to be done at end of sequence
        self.logger.debug("Cleaning up place avatar")
        
        
        """not bothering to sequence these next few."""
        
    def set_up_sim_presence(self):    
        # inialize vars
        self.last_ping = 0
        self.packets = {}
        self.startedEQG = False
        self.logger.debug("Inializing vars for sim presence")
        
    def start_sim_EQG_thread(self):
        #name is what it does
        sim_event_queue = IEventQueueGet(self.region)
        sim_queue_thread = Thread(target=run_sim_eqg, name="Agent Domain event queue", args=(sim_event_queue,))
        sim_queue_thread.start()
            
    def maintain_sim_presence(self):
        # inverted UDP loop from original code --start loop in timer or thread

        msg_buf, msg_size = self.messenger.udp_client.receive_packet(self.messenger.socket)
        packet = self.messenger.receive_check(self.messenger.udp_client.get_sender(),
                                        msg_buf, msg_size)
        if packet != None:
            #print 'Received: ' + packet.name + ' from  ' + self.messenger.udp_client.sender.ip + ":" + \
             #                                 str(self.messenger.udp_client.sender.port)

            #MESSAGE HANDLERS
            if packet.name == 'RegionHandshake':
                self.send_region_handshake_reply(self.vanishing_data['agent_id'], self.vanishing_data['session_id'])
            elif packet.name == 'StartPingCheck':
                self.send_complete_ping_check(self.last_ping)
                self.last_ping += 1                    
                
            if packet.name not in self.packets:
                self.packets[packet.name] = 1
            else: 
                self.packets[packet.name] += 1  
                                
        else:
            #print 'No message'
            pass
            
        """!!! Currently this seems to always be done, some flaw in library code perhaps?"""
        if self.messenger.has_unacked():     
            #print 'Acking'
            self.messenger.process_acks()
            self.send_agent_update(self.vanishing_data['agent_id'], self.vanishing_data['session_id'])
    

        """ jhulriman noted that this returns "TestMessage" packet which isn't part of normal login sequence
        he thinks its due to not zero unencoding packets at the right place"""
        
        return packet.name   
    
    def tearDown(self):
        # Done at end of UDP loop to gracefully log us out (we hope)
        msg = Message('LogoutRequest',
                      Block('AgentData', AgentID=uuid.UUID(self.vanishing_data['session_id']),
                            SessionID=uuid.UUID(self.vanishing_data['session_id'])
                            )
                      )
        self.messenger.send_message(msg, self.host)

    """These should eventually go in their own library I think"""
        
    def send_agent_update(self, agent_id, session_id):
        msg = Message('AgentUpdate',
                  Block('AgentData', AgentID=uuid.UUID(agent_id),
                        SessionID=uuid.UUID(session_id),
                        BodyRotation=(0.0,0.0,0.0,0.0),
                        HeadRotation=(0.0,0.0,0.0,0.0),
                        State=0x00,
                        CameraCenter=(0.0,0.0,0.0),
                        CameraAtAxis=(0.0,0.0,0.0),
                        CameraLeftAxis=(0.0,0.0,0.0),
                        CameraUpAxis=(0.0,0.0,0.0),
                        Far=0,
                        ControlFlags=0x00,
                        Flags=0x00))

        self.messenger.send_message(msg, self.host)
        self.logger.debug("Sent message: Agent Update")

    def send_region_handshake_reply(self, agent_id, session_id):
        msg = Message('RegionHandshakeReply',
                      [Block('AgentData', AgentID=uuid.UUID(agent_id),
                            SessionID=uuid.UUID(self.session_id)),
                       Block('RegionInfo', Flags=0x00)])
    
        self.messenger.send_message(msg, self.host)
        self.logger.debug("Sent message: RegionHandshakeReply")
    
    def send_complete_ping_check(self, ping):
        msg = Message('CompletePingCheck',
                      Block('PingID', PingID=ping))
    
        self.messenger.send_message(msg, self.host)
        self.logger.debug("Sent message: CompletePingCheck")
        
    

        
"""no longer used--replaced by place_avatar sequence above"""
    #def connect(self):
        ##SENDS UseCircuitCode
        #msg = Message('UseCircuitCode',
                      #Block('CircuitCode', Code=self.circuit_code,
                            #SessionID=uuid.UUID(self.session_id),
                            #ID=uuid.UUID(self.agent_id)))
        #self.messenger.send_reliable(msg, host, 0)

        #time.sleep(1)

        ##SENDS CompleteAgentMovement
        #msg = Message('CompleteAgentMovement',
                      #Block('AgentData', AgentID=uuid.UUID(self.agent_id),
                            #SessionID=uuid.UUID(self.session_id),
                            #CircuitCode=self.circuit_code))
        #self.messenger.send_reliable(msg, host, 0)

        ##SENDS UUIDNameRequest
        #msg = Message('UUIDNameRequest',
                      #Block('UUIDNameBlock', ID=uuid.UUID(self.agent_id)
                            #)
                      #)
        #self.messenger.send_message(msg, host)        
       



"""threading calls... May need to use more elaborate EventQueueGet class later on"""
       

def run_ad_eq(ad_event_queue):
    #NOW, event queue to sim
    
    start = time.time()
    now = start
    
    while (now - start > 15):
        result = ad_event_queue()
        #print "Agent Domain queue returned: "
        #pprint.pprint(result)
        
def run_sim_eqg(sim_event_queue):
    
    start = time.time()
    now = start
    
    #while (now - start > 1):
    try:
        result = sim_event_queue()
        print "Sim event queue returned: "
        pprint.pprint(result)
    except Exception, e:
        print "Sim had no events"
        #just means nothing to get
        pass
    


class eventQueueGet(Thread):
    """ spawns a thread in which to run the eventqueueget 
    This class may or may not be needed. I added special code to try toprotect logging class
    from invalid access after window close but that may not be necessary as the simpler class 
    above seems to work fine as well"""
    
    def __init__ (self, eqg, logger):
        
        self.eqg = eqg
        self.logger = logger
        self.contenew = True    #flag to handle thread interupts python-style  --might have better solution in base class--dunno
        Thread.__init__(self)
        
    def run(self):

        self.logger.debug("spawning agent domain event queue thread")
        
        for i in range(1,20):       #in production this would be infinite loop, not for loop
            if self.contenew == False: break   #window is closing so stop thread ASAP
            self.logger.debug("calling EQG cap")
            result = self.eqg()
            if self.contenew == False: break   #no sense in logging the last return--text panel is now inactive
            else:
                self.logger.debug("%s returned: %s", self.getName(), result)   
        #self.logger.debug("ending EQG AD")
        
class TestApp(wx.App):
    def OnInit(self):
        self.savedstdio = sys.stdout
        
        self.loginmethodindex=0
        self.place_avatarindex=0

        self.frame = TestFrame(None, -1, "TestIt")
        self.frame.Show(True)
        
        sys.stdout = sys.stderr = self.frame.errtext
        #logging setup could go here instead...
        
        self.SetTopWindow(self.frame)
        registration.init()           #ZCA stuff?

      
        
        
        return True
    def DoTimer(self,event):
        
        
        if self.loginmethodindex==0: 
            
            self.avatar_obj = avatar_obj(self)   #instantiate the avatar_obj class for login/place goodness
            
            self.loginmethodslen = self.avatar_obj.listerlen(self.avatar_obj.loginmethods) #get the method list lengths
            self.place_avatarmethodslen = self.avatar_obj.listerlen(self.avatar_obj.place_avatarmethods)
            
            #probably logging init code belongs in app __init__ code but oh well...
            
            console = logging.StreamHandler(self.frame.timertext)
            
            self.timerlogger = logging.getLogger("Timer")
            self.timerlogger.setLevel(logging.DEBUG)
            self.timerlogger.addHandler(console)
            self.logger = logging.getLogger("pyogp.lib.base.wxexample")
            
            self.startedUDPhandler = False
            self.udpcounter = 0    #to limit UDP packets for testing purposes. Otherwise rig a "break" from while loop
            
            
        
        
        if self.loginmethodindex <self.loginmethodslen:   #Start login methods sequence
            
            self.avatar_obj.donextmethod(self.avatar_obj.loginmethods,self.loginmethodindex)  
            self.loginmethodindex +=1
        
            
        elif self.place_avatarindex <self.place_avatarmethodslen:   #start place avatar methods sequence
            self.avatar_obj.donextmethod(self.avatar_obj.place_avatarmethods,self.place_avatarindex)  
            self.place_avatarindex +=1
            
                
        elif self.startedUDPhandler == False:             #start UDP packet handler sequence
            self.avatar_obj.set_up_sim_presence()
            self.avatar_obj.start_sim_EQG_thread()
            self.startedUDPhandler = True
            
        elif self.udpcounter<10:   # Go UDP packet handler GO!!!!
            self.logger.info( "udp packet#: " + str(self.udpcounter) + ". Packet name is: " + str(self.avatar_obj.maintain_sim_presence()))
            self.udpcounter+=1
        elif self.udpcounter == 10:   #it done....
            self.logger.info( "OMG did it work???")
            self.udpcounter+=1
            
            

        self.timerlogger.info(  "time = " + str(datetime.datetime.now().microsecond))           

        
   
        
        
       
def wxmain():
    
    app = TestApp(None)

    app.MainLoop()
    
    return


def main():
    return wxmain()

if __name__=="__main__":
    main()
